#include "WiFi.h"
#include "ArduinoJson.h"
#include "HTTPClient.h"
#include "RMaker.h"
#include "WiFiProv.h"

#define DEFAULT_LED_STATE true

// JSON parsing
static DynamicJsonDocument doc(1024);
// HTTP request
HTTPClient http;

// BLE broadcast name and shared secret
const char* service_name = "ESP_Friend";
const char* pop = "1234567";
// API service address
const char* apiServer = "https://api.map.baidu.com/weather/v1/?district_id=310115&data_type=all&ak=YourAK";

// GPIO
static uint8_t LED_1 = 19;
static uint8_t LED_2 = 20;
static bool LED_state_1 = false;
static bool LED_state_2 = false;

// Days of the week
static String week;
// Weather description
static String weathertext;
// Temperature
static int temp;
// Location name
static String name;
// Wind speed
static String wind_class;
// Relative humidity
static int rh;

// Create a Device using the temperature sensor template
static TemperatureSensor weatherReport("今日天气");
// Create a switch Device
static Switch switch_1("开关 1", &LED_state_1);
static Switch switch_2("开关 2", &LED_state_2);
// Create a custom Device
static Device weekReport("星期", NULL, NULL);

// Switch callback function
void write_callback(Device *device, Param *param, const param_val_t val, void *priv_data, write_ctx_t *ctx) {
  // Get the device name and parameter name
  const char* device_name = device->getDeviceName();
  const char* param_name = param->getParamName();

  // Check device name and parameter name
  if(strcmp(device_name, "开关 1") == 0) {
    if(strcmp(param_name, "Power") == 0) {
      // Get the boolean value from the parameter
      LED_state_1 = val.val.b;
      // Turn on the LED if it's true, turn it off if it's false
      digitalWrite(LED_1, LED_state_1 ? HIGH :LOW);
      param->updateAndReport(val);
    }
  }

  // Check device name and parameter name
  if(strcmp(device_name, "开关 2") == 0) {
    if(strcmp(param_name, "Power") == 0) {
      // Get the boolean value from the parameter
      LED_state_2 = val.val.b;
      // Turn on the LED if it's true, turn it off if it's false
      digitalWrite(LED_2, LED_state_2 ? HIGH :LOW);
      param->updateAndReport(val);
    }
  }
}

// Upload data to RainMaker
void sendData() {
  // Convert the temperature parameter from int to float
  float temp_float = temp;
  // Upload weather information
  weatherReport.updateAndReportParam("地址", name.c_str());
  weatherReport.updateAndReportParam("天气", weathertext.c_str());
  weatherReport.updateAndReportParam("风力", wind_class.c_str());
  weatherReport.updateAndReportParam("湿度", rh);
  weatherReport.updateAndReportParam("Temperature", temp_float);
  // Upload week information
  weekReport.updateAndReportParam("星期", week.c_str());
  // Check the temperature and send a warning if it's high
  if(temp > 30) {
    esp_rmaker_raise_alert("高温天气，小心中暑！");
  }
  // Check the weather and send a reminder if it's raining
  if(weathertext.indexOf("雨") != -1) {
    esp_rmaker_raise_alert("下雨了，记得打伞！");
  }
}

// Get weather information from the Baidu Map API
void getWeather(){
  http.begin(apiServer);
  int httpCode = http.GET();
  delay(500);
  // If the request is successful, print the weather information
  if(httpCode == HTTP_CODE_OK) {
    // Get the response data
    String responseData = http.getString();
    // Parse JSON data
    deserializeJson(doc, responseData);
    // Get fields from the JSON object
    JsonObject result = doc["result"];
    JsonObject now = result["now"];
    JsonObject location = result["location"];
    JsonObject forecasts = result["forecasts"][0];

    // Get week information
    week = forecasts["week"].as<String>();
    // Get weather description
    weathertext = now["text"].as<String>();
    // Get temperature
    temp = now["temp"].as<int>();
    // Get wind speed
    wind_class = now["wind_class"].as<String>();
    // Get relative humidity
    rh = now["rh"].as<int>();
    // Get location name
    name = location["name"].as<String>();

    Serial.println("------------天气信息----------------");
    Serial.print("地区：");
    Serial.println(name);
    Serial.print("天气：");
    Serial.println(weathertext);
    Serial.print("温度：");
    Serial.println(temp);
    Serial.print("风力：");
    Serial.println(wind_class);
    Serial.print("相对湿度：");
    Serial.println(rh);
    Serial.println("-----------------------------------");
  }else{
    // Request failed
    Serial.println("Failed to get weather information");
  }
  // Close the HTTP connection
  http.end();
}

void setup() {
  // Set the baud rate to 115200
  Serial.begin(115200);

  // Initialize GPIO
  pinMode(LED_1, OUTPUT);
  pinMode(LED_2, OUTPUT);
  digitalWrite(LED_1, DEFAULT_LED_STATE);
  digitalWrite(LED_2, DEFAULT_LED_STATE);

  // Declare and initialize a Rainmaker node 
  Node my_node;
  my_node = RMaker.initNode("ESP_Friend");

  // Set callback functions for switches
  switch_1.addCb(write_callback);
  switch_2.addCb(write_callback);

  // Create parameters to upload
  Param weather_char("天气", "char", value("default"), PROP_FLAG_WRITE | PROP_FLAG_READ);
  Param rh_int("湿度", "int", value(0), PROP_FLAG_WRITE | PROP_FLAG_READ);
  Param wind_char("风力", "char", value("default"), PROP_FLAG_WRITE | PROP_FLAG_READ);
  Param address_char("地址", "char", value("default"), PROP_FLAG_WRITE | PROP_FLAG_READ);
  Param week_char("星期", "char", value("default"), PROP_FLAG_WRITE | PROP_FLAG_READ);

  // Add parameters to the Device
  weatherReport.addParam(weather_char);
  weatherReport.addParam(rh_int);
  weatherReport.addParam(wind_char);
  weatherReport.addParam(address_char);
  weekReport.addParam(week_char);

  // Set the newly created parameters as primary parameters
  weatherReport.assignPrimaryParam(weatherReport.getParamByName("天气"));
  weekReport.assignPrimaryParam(weekReport.getParamByName("星期"));

  // Add parameters to the Device
  my_node.addDevice(weatherReport);
  my_node.addDevice(weekReport);
  my_node.addDevice(switch_1);
  my_node.addDevice(switch_2);

  // Start Rainmaker 
  RMaker.start();

  // Use Bluetooth provisioning
  WiFiProv.beginProvision(WIFI_PROV_SCHEME_BLE, WIFI_PROV_SCHEME_HANDLER_FREE_BTDM,
                            WIFI_PROV_SECURITY_1, pop, service_name);
}

void loop() {
  // Triggered when the Boot button is pressed
  if(digitalRead(0) == LOW){
    // Get weather information
    getWeather();
    // Upload weather information
    sendData();
    delay(200);
  }
  delay(200);
}